package org.example.service;

import cn.hutool.json.JSONUtil;
import com.aliyun.imagesearch20201214.models.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.example.conf.AlibabaCloudConfig;
import org.example.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

import com.aliyun.imagesearch20201214.Client;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;

import java.io.FileInputStream;
import java.io.InputStream;

@Slf4j
@Service
public class AliImgService {

    @Autowired
    private AlibabaCloudConfig alibabaCloudConfig;
    @Autowired
    private Config config;
    private final static String CHARSET_UTF8 = "utf8";

    private final static String ALGORITHM = "UTF-8";

    public String addImg(Product product) {
        try {
            Client client = new Client(config);
            AddImageAdvanceRequest request = new AddImageAdvanceRequest();
            // 必填，图像搜索实例名称。注意是实例名称不是实例ID。购买后通过 上云层管控台实例信息一栏查看
            request.instanceName = alibabaCloudConfig.getInstanceName();
            // 必填，商品id，最多支持256个字符。
            // 一个商品可有多张图片。
            request.productId = product.getProductId();
            // 必填，图片名称，最多支持256个字符。
            // 1. ProductId + PicName唯一确定一张图片。
            // 2. 如果多次添加图片具有相同的ProductId + PicName，以最后一次添加为准，前面添加的图片将被覆盖。
            request.picName = product.getProductName();
            // 选填，图片类目。
            // 1. 对于商品搜索：若设置类目，则以设置的为准；若不设置类目，将由系统进行类目预测，预测的类目结果可在Response中获取 。
            // 2. 对于布料、商标、通用搜索：不论是否设置类目，系统会将类目设置为88888888。
            request.categoryId = 3;
            // 选填，用户自定义的内容，最多支持4096个字符。
            // 查询时会返回该字段。例如可添加图片的描述等文本。
            request.customContent = "this is a simple test";
            // 选填，整数类型属性，可用于查询时过滤，查询时会返回该字段。
            // 例如不同的站点的图片/不同用户的图片，可以设置不同的IntAttr，查询时通过过滤来达到隔离的目的
//            request.intAttr = 56;
            // 选填，字符串类型属性，最多支持128个字符。可用于查询时过滤，查询时会返回该字段。
//            request.strAttr = "test";
            // 选填，是否需要进行主体识别，默认为true。
            // 1.为true时，由系统进行主体识别，以识别的主体进行搜索，主体识别结果可在Response中获取。
            // 2.为false时，则不进行主体识别，以整张图进行搜索。
            // 3.对于布料图片搜索，此参数会被忽略，系统会以整张图进行搜索。
//            request.crop = true;
            // 选填，图片的主体区域，格式为 x1,x2,y1,y2, 其中 x1,y1 是左上角的点，x2，y2是右下角的点。设置的region 区域不要超过图片的边界。
            // 若用户设置了Region，则不论Crop参数为何值，都将以用户输入Region进行搜索。
            // 对于布料图片搜索，此参数会被忽略，系统会以整张图进行搜索。
//        request.region = "167,467,221,407";
            RuntimeOptions runtimeOptions = new RuntimeOptions();
//        String picName = "D:/123.jpg";
            // 图片内容，最多支持 4MB大小图片以及5s的传输等待时间。当前仅支持PNG、JPG、JPEG、BMP、GIF、WEBP、TIFF、PPM格式图片；
            // 对于商品、商标、通用图片搜索，图片长和宽的像素必须都大于等于100且小于等于4096；
            // 对于布料搜索，图片长和宽的像素必须都大于等于448且小于等于4096；
            // 图像中不能带有旋转信息

            // 下方注释代码是URL方式。
//            String url = "https://www.example.com/123.jpg";
            request.picContentObject = new URL(product.getPicContent()).openStream();

            //下方为通过图片文件上传。
//            InputStream inputStream = new FileInputStream(picName);
//            request.picContentObject = inputStream;
            AddImageResponse response = client.addImageAdvance(request, runtimeOptions);
            System.out.println("success: " + response.getBody().success + ". message: "
                    + response.getBody().message + ". categoryId: "
                    + response.getBody().picInfo.categoryId + ". region:"
                    + response.getBody().picInfo.region
                    + ". requestId: " + response.getBody().requestId);
            return "success: " + response.getBody().success + ". message: "
                    + response.getBody().message + ". categoryId: "
                    + response.getBody().picInfo.categoryId + ". region:"
                    + response.getBody().picInfo.region
                    + ". requestId: " + response.getBody().requestId;
        } catch (TeaException e) {
            System.out.println(e.getCode());
            System.out.println(e.getData());
            System.out.println(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 搜索商品
     * // Type = SearchByPic  说明请求的是SearchImageByPic，使用图片进行搜索。
     * // Type = SearchByPic 时 PicContent 是必填
     * params.add(new BasicNameValuePair("Type", "SearchByPic"));
     * // Type = SearchByName  说明请求的是SearchImageByName，SearchByName
     * // Type = SearchByName 时 ProductId和PicName，PicContent非必填
     * // params.add(new BasicNameValuePair("Type", "SearchByName"));
     * // params.add(new BasicNameValuePair("ProductId", "demo"));
     * // params.add(new BasicNameValuePair("PicName", "demo"));
     *
     * @param pic
     */

    public String searchImage(String pic) {

        //以下为内网（VPC）访问方式
        // 说明：内网（VPC）访问：仅限同区域ECS或资源的访问，例如您购买的图像搜索实例是华东2（上海），那么您的ECS或资源也必须在华东2（上海）才能通过内网VPC地址访问图搜服务，否则会调用不通，如果遇到调用不通，请先检查您的ECS或资源与图像搜索是否在同一个区域。
        // authConfig.endpointType = "internal";  // 如果是内网访问图像搜索服务，则endpointType为必填项，值统一为"internal"
        // authConfig.endpoint = "imagesearch-vpc.cn-shanghai.aliyuncs.com"; // 为内网访问（VPC）地址，请您更换为您购买实例的区域，例如您购买实例的区域为杭州，则endpoint="imagesearch-vpc.cn-hangzhou.aliyuncs.com"

        try {
            Client client = new Client(config);
            SearchImageByPicAdvanceRequest request = new SearchImageByPicAdvanceRequest();
            // 必填，图像搜索实例名称。注意是实例名称不是实例ID。购买后通过 上云层管控台实例信息一栏查看
            request.instanceName = alibabaCloudConfig.getInstanceName();
            // 选填，商品类目。
            // 1. 对于商品搜索：若设置类目，则以设置的为准；若不设置类目，将由系统进行类目预测，预测的类目结果可在Response中获取 。
            // 2. 对于布料、商标、通用搜索：不论是否设置类目，系统会将类目设置为88888888。
            request.categoryId = 3;
            // 选填，返回结果的数目。取值范围：1-100。默认值：10。
            request.num = 10;
            // 选填，返回结果的起始位置。取值范围：0-499。默认值：0。
            request.start = 0;
            // 选填，是否需要进行主体识别，默认为true。
            // 1.为true时，由系统进行主体识别，以识别的主体进行搜索，主体识别结果可在Response中获取。
            // 2.为false时，则不进行主体识别，以整张图进行搜索。
            // 3.对于布料图片搜索，此参数会被忽略，系统会以整张图进行搜索。
            request.crop = true;
            // 选填，图片的主体区域，格式为 x1,x2,y1,y2, 其中 x1,y1 是左上角的点，x2，y2是右下角的点。设置的region 区域不要超过图片的边界。
            // 若用户设置了Region，则不论Crop参数为何值，都将以用户输入Region进行搜索。
            // 3.对于布料图片搜索，此参数会被忽略，系统会以整张图进行搜索。
//            request.region = "167,467,221,407";
            // 选填，过滤条件
            // int_attr支持的操作符有>、>=、<、<=、=，str_attr支持的操作符有=和!=，多个条件之支持AND和OR进行连接。
            // 示例：
            //  1. 根据IntAttr过滤结果，int_attr>=100
            //  2. 根据StrAttr过滤结果，str_attr!="value1"
            //  3. 根据IntAttr和StrAttr联合过滤结果，int_attr=1000 AND str_attr="value1"
//            request.filter = "int_attr=56 AND str_attr=\"test\"";
//            String picName = "D:/123.jpg";
            // 图片内容，最多支持 4MB大小图片以及5s的传输等待时间。当前仅支持PNG、JPG、JPEG、BMP、GIF、WEBP、TIFF、PPM格式图片；
            // 对于商品、商标、通用图片搜索，图片长和宽的像素必须都大于等于100且小于等于4096；
            // 对于布料搜索，图片长和宽的像素必须都大于等于448且小于等于4096；
            // 图像中不能带有旋转信息
            request.num = 10;
            request.start = 0;
            //选填,若为true则响应数据根据ProductId进行返回。
            request.distinctProductId = false;
            RuntimeOptions runtimeObject = new RuntimeOptions();
            // 下方注释代码是URL方式。
            request.picContentObject = new URL(pic).openStream();

            //下方为通过图片文件上传。
//            InputStream inputStream = new FileInputStream(picName);
//            request.picContentObject = inputStream;
            SearchImageByPicResponse response = client.searchImageByPicAdvance(request, runtimeObject);
            System.out.println(response.getBody().requestId);
            System.out.println(response.getBody().picInfo.categoryId);
//            System.out.println(response.getBody().picInfo.region);
            List<SearchImageByPicResponseBody.SearchImageByPicResponseBodyAuctions> auctions = response.getBody().getAuctions();
            System.out.println("多主体信息");
            SearchImageByPicResponseBody.SearchImageByPicResponseBodyPicInfo picInfo = response.getBody().getPicInfo();
            for (SearchImageByPicResponseBody.SearchImageByPicResponseBodyPicInfoMultiRegion multiRegion : picInfo.getMultiRegion()) {
                System.out.println(multiRegion.region);
            }
            for (SearchImageByPicResponseBody.SearchImageByPicResponseBodyAuctions auction : auctions) {
                System.out.println(auction.categoryId + " " + auction.picName + " "
                        + auction.productId + " " + auction.customContent + " " + auction.score + " "
                        + auction.strAttr + " " + auction.intAttr);
            }
            return JSONUtil.toJsonStr(auctions);
        } catch (TeaException e) {
            System.out.println(e.getCode());
            System.out.println(e.getData());
            System.out.println(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }


    public String deleteImage(Product product) {
        try {
            //以下为内网（VPC）访问方式
            // 说明：内网（VPC）访问：仅限同区域ECS或资源的访问，例如您购买的图像搜索实例是华东2（上海），那么您的ECS或资源也必须在华东2（上海）才能通过内网VPC地址访问图搜服务，否则会调用不通，如果遇到调用不通，请先检查您的ECS或资源与图像搜索是否在同一个区域。
            // authConfig.endpointType = "internal";  // 如果是内网访问图像搜索服务，则endpointType为必填项，值统一为"internal"
            // authConfig.endpoint = "imagesearch-vpc.cn-shanghai.aliyuncs.com"; // 为内网访问（VPC）地址，请您更换为您购买实例的区域，例如您购买实例的区域为杭州，则endpoint="imagesearch-vpc.cn-hangzhou.aliyuncs.com"
            Client client = new Client(config);
            DeleteImageRequest request = new DeleteImageRequest();
            // 必填，图像搜索实例名称。注意是实例名称不是实例ID。购买后通过 上云层管控台实例信息一栏查看
            request.instanceName = alibabaCloudConfig.getInstanceName();
            // isDeleteByFilter字段为false则必填，商品id，最多支持 512个字符。
            // 一个商品可有多张图片。仅指定该参数不指定picName，结果可返回删除的所有picname内容
            request.productId = "test";
            // 选填，图片名称。若不指定本参数，则删除ProductId下所有图片；若指定本参数，则删除ProductId+PicName指定的图片。
            request.picName = "1000";
            //选填,若为true则根据filter进行删除。
            request.isDeleteByFilter = false;
            request.filter = "intattr3=xxx";
            DeleteImageResponse response = client.deleteImage(request);
            System.out.println(response.getBody().toMap());
        } catch (TeaException e) {
            System.out.println(e.getCode());
            System.out.println(e.getData());
            System.out.println(e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }


    private static byte[] getBytes(String filePath) {
        byte[] buffer = null;
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            // picture max size is 2MB
            ByteArrayOutputStream bos = new ByteArrayOutputStream(2000 * 1024);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    private static String convert(InputStream inputStream, Charset charset) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        String line;
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, charset));
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
        } finally {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }
        return stringBuilder.toString();
    }


    private static String buildSignatureStr(String operation, String postContent, Map<String, String> headers) {
        String data = "POST\n";
        String accept = "application/json";
        data += accept + "\n";

        String contentMd5;
        try {
            contentMd5 = getMd5(postContent);
        } catch (Exception e) {
            return null;
        }
        data += contentMd5 + "\n";

        String contentType = "application/x-www-form-urlencoded; charset=" + ALGORITHM;
        data += contentType + "\n";

        String gmt = getGMT();
        data += gmt + "\n";

        String method = "HMAC-SHA1";
        data += "x-acs-signature-method:" + method + "\n";

        String signatureNonce = generateSignatureNonce();
        data += "x-acs-signature-nonce:" + signatureNonce + "\n";

        String apiVersion = "2019-03-25";
        data += "x-acs-version:" + apiVersion + "\n";
        data += "/v2/image/" + operation;

        headers.put("x-acs-version", apiVersion);
        headers.put("x-acs-signature-method", method);
        headers.put("x-acs-signature-nonce", signatureNonce);

        headers.put("accept", accept);
        headers.put("content-md5", contentMd5);
        headers.put("content-type", contentType);
        headers.put("date", gmt);

        return data;
    }

    private static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
        if (secret == null || secret.length() == 0) {
            throw new IOException("secret can not be empty");
        }
        if (baseString == null || baseString.length() == 0) {
            return null;
        }
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
        mac.init(keySpec);
        return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
    }

    private static String newStringByBase64(byte[] bytes)
            throws UnsupportedEncodingException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new String(org.apache.commons.codec.binary.Base64.encodeBase64(bytes, false), CHARSET_UTF8);
    }


    private static String getGMT() {
        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdf.format(cd.getTime());
    }

    private static String generateSignatureNonce() {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 32; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    private static String getMd5(String body) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        Base64 base64en = new Base64();
        return base64en.encodeToString(md5.digest(body.getBytes(CHARSET_UTF8)));

    }

}
